package com.zhl.study.annotation;

import cn.hutool.json.JSONUtil;
import com.zhl.study.enums.PreventStrategy;
import com.zhl.study.util.decode.Base64Utils;
import com.zhl.study.util.decode.MD5Util;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @description 防止重复刷新切面
 * @Author: z156940l@outlook.com
 * @className PreventAop
 * @Date: 2023/4/28 15:21
 */
@Aspect
@Component
public class PreventAop {

    @Autowired
    StringRedisTemplate redisTemplate;

    private final String anotherStr = "null";

    /**
     * 切入点
     */
    @Pointcut("@annotation(com.zhl.study.annotation.Prevent)")
    public void pointcut(){}

    /**
     * 前置通知
     * @param joinPoint
     * @throws Exception
     */
    @Before("pointcut()")
    public void joinPoint(JoinPoint joinPoint) throws Exception{
        String requestStr = JSONUtil.toJsonStr(joinPoint.getArgs()[0]);
        if (StringUtils.isEmpty(requestStr) || requestStr.equalsIgnoreCase(anotherStr)){
            throw new RuntimeException("[防刷]入参不能为空");
        }

        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();

        Method method = joinPoint.getTarget().getClass().getMethod(methodSignature.getName(),
                methodSignature.getParameterTypes());

        Prevent preventAnnotation = method.getAnnotation(Prevent.class);

        String methodFullName = method.getDeclaringClass().getName() + method.getName();

        entrance(preventAnnotation, requestStr, methodFullName);
    }

    /**
     * 入口
     * @param prevent
     * @param requestStr
     * @param methodFullName
     * @throws Exception
     */
    private void entrance(Prevent prevent, String requestStr, String methodFullName) throws RuntimeException{
        PreventStrategy strategy = prevent.strategy();
        switch (strategy){
            case DEFAULT:
                defaultHandle(requestStr, prevent, methodFullName);
                break;
            default:
                throw new RuntimeException("无效策略");
        }
    }

    private void defaultHandle(String requestStr, Prevent prevent, String methodFullName){
        //不要介意Base64Utils.ImageToBase64ByLocal这个方法名，就做一次base64加密处理而已
        String base64Str = MD5Util.md5Base64Salt(requestStr);
        long expire = Long.parseLong(prevent.value());

        String resp = redisTemplate.opsForValue().get(methodFullName + base64Str);
        if (StringUtils.isEmpty(resp)){
            redisTemplate.opsForValue().set(methodFullName + base64Str, requestStr, expire, TimeUnit.SECONDS);
        }else {
            String message = !StringUtils.isEmpty(prevent.message()) ? prevent.message() :
                    expire + "秒内不允许重复请求";
            throw new RuntimeException(message);
        }
    }
}
